package com.yyj.ws;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yyj.pojo.Message;
import com.yyj.util.MessageUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 浏览器-->服务器：{"toName":"张三","message":"你好"}
 * 服务器-->浏览器：系统消息格式{"isSystem":true,"fromName":null,"message":["李四","王五"]}
 *                  推送某一个消息格式{"isSystem":false,"fromName":张三,"message":"你好"}
*   对应chat.js的var ws = new WebSocket("ws://localhost:8088/chat");
 */
@ServerEndpoint(value = "/chat", configurator = GetHttpSessionConfigurator.class)
@Component
public class ChatEndpoint {
//每创建一个用户都对应一个ChatEndpoint，张三，李四，王五各自都对应一个ChatEndpoint对象，我们就可以使用@OnOpen的Session获取发送数据对象

    private final static Logger LOGGER = LogManager.getLogger(ChatEndpoint.class);

    /**
     * 用来储存在线用户的容器
     * 静态的只有一个，因为ChatEndpoint对象可以有很多个，我们只需要一个容器就行
     * 键是名字，值是对应的ChatEndpoint对象
     */
    public static Map<String, ChatEndpoint> onlineUsers = new ConcurrentHashMap<>();

    /**
     * 声明Session对象，通过该对象可以发送消息给指定的用户
     * 用来给客户端发送消息
     */
    private Session session;

    /**
     * 我们之前在HttpSession对象中存储了用户名
     * 用来获取在登录成功后，放在httpsession域中存放的username
     */
    private HttpSession httpSession;

    /*建立时被调用*/
    @OnOpen
    public void onOpen(Session session, EndpointConfig endpointConfig) {
        LOGGER.info("wbsocket建立连接onOpen！");
        //将局部session对象复制给成员对象session
        this.session = session;
        //获取httpsession对象，去看GetHttpSessionConfigurator类里的写法
        HttpSession httpSession = (HttpSession) endpointConfig.getUserProperties().get(HttpSession.class.getName());
        this.httpSession = httpSession;
        //获取httpsession域中存放的username对应的值
        String username = (String) httpSession.getAttribute("username");
        //存放到onlineUsers中保存在线用户
        onlineUsers.put(username, this);

        //系统消息推送所有在线用户给客户端，将当前所有在线用户的用户名推送给所有的客户端
        //服务器-->客户端(浏览器)推送消息  封装系统推送消息,前端onmessage接收的数据
        String message = MessageUtils.getMessage(true, null, getAllOnlineUsername());
        broadcastMsgToAllOnlineUsers(message);//调用方法进行系统消息的推送
    }

    /**
     * .
     * @param message 给客户端发送消息
     * @return void
     */
    private void broadcastMsgToAllOnlineUsers(String message) {
        //所有登录用户名称
        Set<String> names = onlineUsers.keySet();
        for (String name : names) {
            ChatEndpoint chatEndpoint = onlineUsers.get(name);
            //获取推送对象
            RemoteEndpoint.Basic basicRemote = chatEndpoint.session.getBasicRemote();
            try {
                basicRemote.sendText(message);
            } catch (IOException e) {
                LOGGER.error("广播发送系统消息失败！{}", e);
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取发送给客户端的消息，消息数据示例值：{“systemMsgFlag”: true, "fromName": null, "message": ["Name1", "Name2"]}.
     * @return java.lang.Object
     */
    private Object getAllOnlineUsername() {
        return ChatEndpoint.onlineUsers.keySet();
    }

    /**
     * 接收到客户端发送的数据时调用.然后对指定用户发送消息
     * @param message 客户端发送的数据
     * @param session session对象
     * @return void
     */
    @OnMessage  //点击发送后，看chat.js里的$("#submit")代码，服务端接收客户端发送的数据后该方法被调用
    public void onMessage(String message, Session session) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            Message msg = objectMapper.readValue(message, Message.class);//将message转换成Message对象
            String toName = msg.getToName();//获取接收信息的用户
            String msgData = msg.getMessage();//获取发送的消息
            String username = (String) httpSession.getAttribute("username");//获取当前登录的用户
            String sendMsg = MessageUtils.getMessage(false, username, msgData);//封装发送的消息
            onlineUsers.get(toName).session.getBasicRemote().sendText(sendMsg);//发送消息
        } catch (JsonProcessingException e) {
            LOGGER.error("接收客户端的消息，转换出错了！");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**关闭时调用*/
    @OnClose
    public void onClose(Session session) {
        String username = (String) httpSession.getAttribute("username");
        //
        ChatEndpoint remove = onlineUsers.remove(username);
        //广播
        String message = MessageUtils.getMessage(true, null, getAllOnlineUsername());
        broadcastMsgToAllOnlineUsers(message);
    }

}
